//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	ListServiceCache.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

#define TRACE_CACHE

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading;
using System.Web.Caching;

namespace Smart.Common.Entities
{
	/// <summary>
	/// List service cache
	/// </summary>
	[Localizable(false)]
	public static class ListServiceCache
	{
		/// <summary>
		/// Need to ensure static fields are filled. DO NOT REMOVE!
		/// </summary>
		static ListServiceCache()
		{
			//  DO NOT REMOVE!
		}

		/// <summary>
		/// List cache sync lock
		/// </summary>
		private static readonly ReaderWriterLockSlim SyncLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		/// <summary>
		/// Remove all list service cache
		/// </summary>
		public static void Clear()
		{
			SPServices.Cache.Remove(string.Concat(Const.Cache.ListServiceKey, "-"));
		}

		/// <summary>
		/// Remove all list service cache
		/// </summary>
		public static void Clear(string facadeName)
		{
			SPServices.Cache.Remove(
				SPServices.Cache.GetKey(
					new object[] { Const.Cache.ListServiceKey, facadeName.ToLower().GetHashCodeX() },
					true, false, false, false,
					null, null));
		}

		/// <summary>
		/// Loads items from cache using query
		/// </summary>
		public static IEntityList<TEntityType> LoadEntities<TEntityType>(MetaList meta, IListService<TEntityType> listService, string query, int rowLimit,
																		 Dictionary<string, object> properties, string facadeName, bool cacheUserDependent, bool cacheUrlDependent,
																		 TimeSpan cacheAbsoluteTimeout, TimeSpan cacheSlidingExpiration, CacheItemPriority cachePriority,
																		 Func<IEntityList<TEntityType>> loader)
			where TEntityType : Entity
		{
			IEntityList<TEntityType> res = null;

			var fullKey = SPServices.Cache.GetKey(
				new object[] { Const.Cache.ListServiceKey, facadeName.ToLower().GetHashCodeX() },
				true, false, cacheUserDependent, cacheUrlDependent,
				query, new object[] { meta.ID.GetHashCodeX(), DictionaryExts.GetValuesHashCodeX(properties) });

			SyncLock.EnterUpgradeableReadLock();

			try
			{
				var cache = SPServices.Cache.Get(fullKey, null) as IEntityList;

				if (cache == null)
				{
#if TRACE_CACHE
					H.LT("ListServiceCache: miss {0}", fullKey);
#endif
					SyncLock.EnterWriteLock();

					try
					{
						res = loader();

						SPServices.Cache.Set(fullKey, res.CloneForCache(), cacheAbsoluteTimeout, cacheSlidingExpiration, cachePriority);
					}
					finally
					{
						SyncLock.ExitWriteLock();
					}
				}
				else
				{
#if TRACE_CACHE
					H.LT("ListServiceCache: HIT  {0}", fullKey);
#endif

					res = new EntityList<TEntityType>(meta, listService);

					res.CloneFromCache((IEntityList<TEntityType>)cache);
				}
			}
			finally
			{
				SyncLock.ExitUpgradeableReadLock();
			}

			return res;
		}
	}
}